package shortestpath.astar;

import java.util.HashSet;
import java.util.Random;
import java.util.Set;

import shortestpath.Edge;
import shortestpath.Graph;
import shortestpath.astar.node.Field;

public class FieldGraph implements Graph<Field> {

	final int xsize;
	final int ysize;
	final Field[][] fields;
	Set<Edge<Field>> edges = null;

	FieldGraph(int xsize, int ysize) {
		super();
		this.xsize = xsize;
		this.ysize = ysize;
		this.fields = new Field[xsize][ysize];

		final Random r = new Random();
		for (int i = 0; i < xsize; i++) {
			for (int j = 0; j < ysize; j++) {
				this.fields[i][j] = new Field(i + "|" + j, r.nextInt(8) + 1, i, j);
			}
		}

	}

	@Override
	public Edge<Field> getEdge(Field a, Field b) {
		if (Math.abs(a.getX() - b.getX()) + Math.abs(a.getY() - b.getY()) != 1) {
			return null;
		} else {
			return new Edge<Field>(b.getPenalty(), a, b);
		}
	}

	@Override
	public Set<Field> getLinkedNodes(Field n) {
		final Set<Field> result = new HashSet<Field>();

		if (n.getX() - 1 > 0) {
			result.add(this.fields[n.getX() - 1][n.getY()]);
		}
		if (n.getX() + 1 < this.xsize) {
			result.add(this.fields[n.getX() + 1][n.getY()]);
		}
		if (n.getY() - 1 > 0) {
			result.add(this.fields[n.getX()][n.getY() - 1]);
		}
		if (n.getY() + 1 < this.ysize) {
			result.add(this.fields[n.getX()][n.getY() + 1]);
		}
		return result;
	}

	@Override
	public Set<Field> getNodes() {
		final Set<Field> result = new HashSet<Field>();
		for (Field[] row : this.fields) {
			for (Field f : row) {
				result.add(f);
			}
		}
		return result;
	}

	public Field getField(int row, int col) {
		return this.fields[row][col];
	}

	@Override
	public String toString() {
		final StringBuilder resultBuilder = new StringBuilder();
		for (Field[] row : this.fields) {
			for (Field f : row) {
				resultBuilder.append(f.getPenalty()).append(' ');
			}
			resultBuilder.append('\n');
		}
		return resultBuilder.toString();
	}

	@Override
	public Set<Edge<Field>> getEdges() {
		if (this.edges == null) {
			final Set<Edge<Field>> result = new HashSet<Edge<Field>>();

			for (int i = 0; i < this.xsize; i++) {
				for (int j = 0; j < this.ysize; j++) {
					if (i - 1 > 0) {
						result.add(new Edge<Field>(this.fields[i - 1][j]
								.getPenalty(), this.fields[i][j],
								this.fields[i - 1][j]));
					}
					if (i + 1 < this.xsize) {
						result.add(new Edge<Field>(this.fields[i + 1][j]
								.getPenalty(), this.fields[i][j],
								this.fields[i + 1][j]));
					}
					if (j - 1 > 0) {
						result.add(new Edge<Field>(this.fields[i][j - 1]
								.getPenalty(), this.fields[i][j],
								this.fields[i][j - 1]));
					}
					if (j + 1 < this.ysize) {
						result.add(new Edge<Field>(this.fields[i][j + 1]
								.getPenalty(), this.fields[i][j],
								this.fields[i][j + 1]));
					}
				}
			}
			this.edges = result;
		}

		return this.edges;
	}

}
